-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[clang][SYCL] Add sycl_external attribute and restrict emitting device code #140282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this @schittir! I completed an initial pass of all of the code, but still need to look more closely at the documentation updates.
Thank you for the initial pass review, Tom! |
✅ With the latest revision this PR passed the C/C++ code formatter. |
Support for functions is sufficient for SYCL 2020 spec conformance.
Co-authored-by: Mariya Podchishchaeva <mariya.podchishchaeva@intel.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @schittir, here are my initial review comments. I expect to review the newly added tests more closely once these comments are all addressed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional comments for diagnostics and tests.
There seems to be an issue with the use of sycl_kernel_entry_point attribute vs __builtin_unique_stable_name
Oops new tests are failing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good @schittir! I added some comments for minor issues. I still need to review the tests more closely.
clang/lib/Sema/SemaDecl.cpp
Outdated
// SYCL spec 2020 | ||
// The first declaration of a function with external linkage must | ||
// specify sycl_external attribute. | ||
// Subsequent declarations may optionally specify this attribute. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is helpful to quote the specification verbatim so that the intent is understood from the quoted context in the event that the specification changes.
// SYCL spec 2020 | |
// The first declaration of a function with external linkage must | |
// specify sycl_external attribute. | |
// Subsequent declarations may optionally specify this attribute. | |
// SYCL 2020 section 5.10.1, "SYCL functions and member functions linkage": | |
// When a function is declared with SYCL_EXTERNAL, that macro must be | |
// used on the first declaration of that function in the translation unit. | |
// Redeclarations of the function in the same translation unit may | |
// optionally use SYCL_EXTERNAL, but this is not required. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the suggestion.
I will implement the wording as it is except for one point - wouldn't it be better to use sycl_external
attribute instead of SYCL_EXTERNAL
macro which is not yet implemented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend towards quoting the text verbatim. Readers are likely to be aware that the intent of the sycl_external
attribute is to implement the SYCL_EXTERNAL
macro; that was made clear in the documentation for the attribute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more comments. I still haven't finished my review of sycl-external-attr.cpp
, but will try to later today or over the weekend.
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s | ||
|
||
// expected-error@+1{{'clang::sycl_external' attribute only applies to functions}} | ||
[[clang::sycl_external]] int a; | ||
|
||
|
||
// expected-error@+2{{'clang::sycl_external' attribute only applies to functions}} | ||
struct s { | ||
[[clang::sycl_external]] int b; | ||
}; | ||
|
||
// expected-error@+1{{'clang::sycl_external' attribute takes no arguments}} | ||
[[clang::sycl_external(3)]] void bar() {} | ||
|
||
// FIXME: The first declaration of a function is required to have the attribute. | ||
// The attribute may be optionally present on subsequent declarations | ||
int foo(int c); | ||
|
||
[[clang::sycl_external]] void foo(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These examples are all grammatically correct because the tokens form a valid attribute-specifier and are placed in a grammatically correct position in each declaration. The only part of the syntax that would be grammatically specific to sycl_external
would be the syntax of its arguments; if it took any. I think it is reasonable to keep (invalid) examples like bar()
that exercise arguments here since, if the attribute were extended to accept an argument, this would be the right place to validate them. I think these are good examples to test in this file:
[[clang::sycl_external()]] void bad1();
[[clang::sycl_external(,)]] void bad2();
[[clang::sycl_external(3)]] void bad3();
[[clang::sycl_external(4,)]] void bad4();
The a
and b
examples exercise appertainment and I think would best be placed in a sycl-external-attr-appertainment.cpp
test.
The foo()
example is redundant; similar declarations are validated by sycl-external-attr.cpp
.
def err_sycl_attribute_avoid_main : Error< | ||
"'sycl_external' cannot be applied to the 'main' function">; | ||
def err_sycl_attribute_avoid_deleted_function | ||
: Error<"'sycl_external' cannot be applied to an explicitly deleted " | ||
"function">; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest using "invalid" in place of "avoid" for consistency with other nearby diagnostics. Please keep the wrapping and indentation style consistent with nearby code.
def err_sycl_attribute_avoid_main : Error< | |
"'sycl_external' cannot be applied to the 'main' function">; | |
def err_sycl_attribute_avoid_deleted_function | |
: Error<"'sycl_external' cannot be applied to an explicitly deleted " | |
"function">; | |
def err_sycl_attribute_invalid_main : Error< | |
"'sycl_external' cannot be applied to the 'main' function">; | |
def err_sycl_attribute_invalid_deleted_function : Error< | |
"'sycl_external' cannot be applied to an explicitly deleted function">; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wrapping and indentation style was modified by git-clang-format tool, which surprised me! I'll revert it.
@@ -0,0 +1,12 @@ | |||
// RUN: %clang_cc1 -fsyntax-only -verify %s | |||
|
|||
// expected-warning@+1{{'clang::sycl_external' attribute ignored}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// expected-warning@+1{{'clang::sycl_external' attribute ignored}} | |
// These tests validate that the sycl_external attribute is ignored when SYCL | |
// support is not enabled. | |
// expected-warning@+1{{'clang::sycl_external' attribute ignored}} |
// expected-warning@+2{{'clang::sycl_external' attribute ignored}} | ||
namespace not_sycl { | ||
[[clang::sycl_external]] void foo() {} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the namespace scoped example adds anything here, but I think it would be good to exercise a function template.
// expected-warning@+2{{'clang::sycl_external' attribute ignored}} | |
namespace not_sycl { | |
[[clang::sycl_external]] void foo() {} | |
} | |
// expected-warning@+2{{'clang::sycl_external' attribute ignored}} | |
template<typename> | |
[[clang::sycl_external]] void ft(T) {} | |
template void ft(int); |
@@ -0,0 +1,70 @@ | |||
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s | |||
// RUN: %clang_cc1 -fsycl-is-device -std=c++20 -fsyntax-only -verify -DCPP20 %s | |||
// Semantic tests for sycl_external attribute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add spacing to avoid potential merge conflicts or careless additions of additional RUN lines in the future.
// Semantic tests for sycl_external attribute | |
// Semantic tests for the sycl_external attribute. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I finished an initial pass through all of the tests. I'll do another round once you've had a chance to catch up on the comments so far.
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s | ||
// RUN: %clang_cc1 -fsycl-is-device -std=c++20 -fsyntax-only -verify -DCPP20 %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency and in case the default changes some day.
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s | |
// RUN: %clang_cc1 -fsycl-is-device -std=c++20 -fsyntax-only -verify -DCPP20 %s | |
// RUN: %clang_cc1 -fsycl-is-device -std=c++17 -fsyntax-only -verify -DCPP17 %s | |
// RUN: %clang_cc1 -fsycl-is-device -std=c++20 -fsyntax-only -verify -DCPP20 %s |
[[clang::sycl_external]] // expected-error {{'sycl_external' can only be applied to functions with external linkage}} | ||
void func4(UnnX) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good test. I suggest trying to keep the tests self-contained as much as possible so that they are easy to review without having to look elsewhere. A consistent naming scheme can help to avoid unintended reuse of symbols that can potentially affect test outcomes.
[[clang::sycl_external]] // expected-error {{'sycl_external' can only be applied to functions with external linkage}} | |
void func4(UnnX) {} | |
// expected-error@+2 {{'sycl_external' can only be applied to functions with external linkage}} | |
namespace { struct S4 {}; } | |
[[clang::sycl_external]] void func4(S4) {} |
|
||
[[clang::sycl_external]] // expected-error {{'sycl_external' can only be applied to functions with external linkage}} | ||
void func4(UnnX) {} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other interesting internal linkage cases:
The attribute from the primary template is inherited by the specialization instantiated by the explicit template instantiation declaration. It could be argued that, because the explicit instantiation declaration names the symbol that causes the instantiated specialization to have internal linkage, a diagnostic should be produced here. However, other template shenanigans can produce similar results and would be difficult to diagnose well. I think we should ensure that a diagnostic is not issued for this case.
namespace { struct S6 {}; }
template<typename>
[[clang::sycl_external]] void func6() {}
template void func6<S6>();
In this case, the attribute from the primary template is not inherited by the specialization produced by the explicit specialization declaration. No diagnostic should be issued for this case. However, Clang doesn't implement the attribute inheritance semantics correctly (according to the standard), so if a diagnostic is issued, we should just add a FIXME comment and not worry about it.
namespace { struct S7 {}; }
template<typename>
[[clang::sycl_external]] void func7();
template<> void func7<S7>() {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is another case. DPC++ does diagnose this one. https://godbolt.org/z/9E9aPn6eq
namespace { struct S8 {}; }
template<typename>
void func8();
template<> [[clang::sycl_external]] void func8<S8>() {}
// The first declaration of a SYCL external function is required to have this attribute. | ||
int foo(); // expected-note {{previous declaration is here}} | ||
|
||
[[clang::sycl_external]] int foo(); // expected-error {{'clang::sycl_external' attribute does not appear on the first declaration}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid blank lines between declarations that are part of the same test so that it is easy to see that they go together. Use line markers and references in expected diagnostics to avoid line length issues.
// The first declaration of a SYCL external function is required to have this attribute. | |
int foo(); // expected-note {{previous declaration is here}} | |
[[clang::sycl_external]] int foo(); // expected-error {{'clang::sycl_external' attribute does not appear on the first declaration}} | |
// The first declaration of a SYCL external function is required to have this attribute. | |
// expected-error@+3 {{'clang::sycl_external' attribute does not appear on the first declaration}} | |
// expected-note@#func8-decl {{previous declaration is here}} | |
int func8(); // #func8-decl | |
[[clang::sycl_external]] int func8(); |
class D { | ||
[[clang::sycl_external]] void del() = delete; // expected-error {{'sycl_external' cannot be applied to an explicitly deleted function}} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test doesn't require a member function, but the presence of class D
suggests that there is something specific to member functions here.
class D { | |
[[clang::sycl_external]] void del() = delete; // expected-error {{'sycl_external' cannot be applied to an explicitly deleted function}} | |
}; | |
// expected-error {{'sycl_external' cannot be applied to an explicitly deleted function}} | |
[[clang::sycl_external]] void del() = delete; |
[[clang::sycl_external]] | ||
A() {} | ||
|
||
[[clang::sycl_external]] void func3() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate a static member function too.
[[clang::sycl_external]] void func3() {} | |
[[clang::sycl_external]] void mf(); | |
[[clang::sycl_external]] static void smf(); |
[[clang::sycl_external]] int *func0() { return nullptr; } | ||
|
||
[[clang::sycl_external]] void func2(int *) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume these are intended to exercise the generic address space limitations. Let's add a comment to make that clear. I don't know if any of the target devices we expect to support lack support for the generic address space, so we might not be able to effectively test a positive diagnostic, but it is good to ensure no diagnostic for the common case.
[[clang::sycl_external]] int *func0() { return nullptr; } | |
[[clang::sycl_external]] void func2(int *) {} | |
// Devices that do not support the generic address space shall not specify | |
// a raw pointer or reference type as the return type or as a parameter type. | |
[[clang::sycl_external]] int *func0(); | |
[[clang::sycl_external]] int &func1(); | |
[[clang::sycl_external]] int &&func2(); | |
[[clang::sycl_external]] void func3(int *); | |
[[clang::sycl_external]] void func4(int &); | |
[[clang::sycl_external]] void func5(int &&); | |
template<typename T> | |
[[clang::sycl_external]] void func6(T) {} | |
template void func6(int *); | |
template<> [[clang::sycl_external]] void func6<int*>(int *) {} |
This patch is part of the upstreaming effort for supporting SYCL language front end.
It makes the following changes: